﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using Microsoft.Practices.ServiceLocation;
using UIExample.View;
using Xceed.Wpf.DataGrid;
using Expression = NCalc.Expression;

namespace UIExample.ViewModel
{
    public class UMPViewModel : ViewModelBase
    {
        private WK35705XML.UMP _ump;
        private bool _visual=true;
        private Thickness _margin;
        private List<UMPLink> _links = new List<UMPLink>();

        public UMPViewModel()
        {
            var a = new Action<NotificationMessage>(message =>
            {
                if (message.Notification == null || InSelf(message.Sender)) Visual = true;
                else if (message.Sender is WK35705XML.UMPOutput) Visual = HasInput(message.Notification);
                else if (message.Sender is WK35705XML.UMPInput) Visual = HasOutput(message.Notification);
            });
            MessengerInstance.Register(this, a );
            Inputs = new ObservableCollection<IOParameterViewModel>();
            Outputs = new ObservableCollection<IOParameterViewModel>();
            
        }

        public void AddLink(UMPLink l)
        {
            if(_links.Contains(l))
                return;
            _links.Add(l);
            l.PropertyChanged += l_PropertyChanged;
        }

        void l_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            var s = sender as UMPLink;
            if (s != null && s.Remove == true)
                _links.Remove(s);
        }

        private bool InSelf(object sender)
        {
            return UMP.ChildObjects.Contains(sender);
        }

        public ObservableCollection<IOParameterViewModel> Inputs { get; set; }
        public ObservableCollection<IOParameterViewModel> Outputs { get; set; }

        private bool HasOutput(string notification)
        {
            return UMP.Output.Any(t => t.type == notification);
        }

        private bool HasInput(string notification)
        {
            return UMP.Input.Any(t => t.type == notification);
        }

        public WK35705XML.UMP UMP
        {
            get { return _ump; }
            set
            {
                if (_ump != value)
                _ump = value;
                Inputs.Clear();
                Outputs.Clear();
                AddIO(value);
                RaisePropertyChanged("UMP");
            }
        }

        private void AddIO(WK35705XML.UMP ump)
        {
            foreach (var i in ump.Input)
            {
                Inputs.Add(new IOParameterViewModel(i,this));
            }
            foreach (var i in ump.Output)
            {
                Outputs.Add(new IOParameterViewModel(i,this));
            }
        }

        public bool Visual
        {
            get { return _visual; }
            set
            {
                if (_visual == value)
                    return;
                _visual = value;
                RaisePropertyChanged("Visual");
            }
        
        }
        public string Equations
        {
            get
            {
                var e = "";
                var m =  ServiceLocator.Current.GetInstance<MainViewModel>();
                foreach (var eq in UMP.Transformation.Equation.Where(s=>s.set == m.ActiveSet))
                {
                    e = eq.Value.Replace("\t",""); 
                }
                return e;


            }
        }
        public string ProcessInformation
        {
            get
            {
                var e = "";
                foreach (var p in UMP.ProcessParameter)
                {
                    e += p.name + " = " +  p.value + " " + p.unit + "\n";
                }
                return e;


            }
        }

        public Thickness Margin
        {
            get { return _margin; }
            set
            {
                if(_margin == value)
                    return;
                _margin = value;
                RaisePropertyChanged("Margin");
                
            }
        }

        public IEnumerable<IOParameterViewModel> IO => Inputs.Union(Outputs);


        public void Calculate()
        {
            if (UMP == null || string.IsNullOrWhiteSpace(Equations))
                return;
            
            try
            {
                foreach (var equation in Equations.Split(';'))
                {
                    var indep = equation.Split('=');
                    if (indep.Count() == 2)
                    {
                        var io = IO.FirstOrDefault(t => t.IO.name == indep[0].Trim('\n').Trim());
                        if (io != null)
                        {
                            io.Value = Calculate(indep[1]);
                        }
                    }

                }
                foreach (var umpLink in _links)
                {
                    if (umpLink.In.ParentViewModel == this)
                    {
                        if (!umpLink.Visited && !umpLink.In.BlockChangingOther)
                        {
                            var d = _links.Count(t => t.In == umpLink.In);
                            umpLink.Value = umpLink.In.Value/d;
                            umpLink.Out.UpdateValue();
                        }
                    }
                    else if (umpLink.Out.ParentViewModel == this)
                    {
                        if (!umpLink.Visited && !umpLink.Out.BlockChangingOther)
                        {
                            var d = _links.Count(t => t.Out == umpLink.Out);
                            umpLink.Value = umpLink.Out.Value/d;
                            umpLink.In.UpdateValue();
                        }
                    }

                }
 

            }
            catch
            {
            }
        }

        private decimal Calculate(string equation)
        {
            var e = equation.Split(' ');
            foreach (IOParameterViewModel input in Inputs)
            {
                for (int index = 0; index < e.Length; index++)
                {
                    if (e[index] == input.IO.name)
                        e[index] = input.Value + " ";
                }
            }
            foreach (IOParameterViewModel output in Outputs)
            {
                for (int index = 0; index < e.Length; index++)
                {
                    if (e[index] == output.IO.name)
                        e[index] = output.Value + " ";
                }
            }
            foreach (var par in UMP.ProcessParameter)
            {
                for (int index = 0; index < e.Length; index++)
                {
                    if (e[index] == par.name)
                        e[index] = par.value + " ";
                }
            }
            try
            {
                var expression = new Expression(e.Aggregate("", (current, s) => current + s));
                var res = expression.Evaluate();
                return  decimal.Parse(res.ToString());
            }
            catch{}
            return -1;
        }

        public void Refresh()
        {
            RaisePropertyChanged("Equations");
            RaisePropertyChanged("ProcessInformation");
            foreach (var ioParameterViewModel in IO)
            {
                ioParameterViewModel.Refresh();
            }
        }

        public List<UMPLink> GetLinks(IOParameterViewModel ioParameterViewModel)
        {
            if (_links != null)
               return _links.Where(t => t.In == ioParameterViewModel || t.Out == ioParameterViewModel).ToList();
            return new List<UMPLink>();
        }
    }
}
